#ifndef __MY_UTIL__
#define __MY_UTIL__

#include <iostream>
#include <fstream>
#include <ostream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <mutex>
#include <jsoncpp/json/json.h>
#include <experimental/filesystem>
#include "bundle.h"

namespace cloud
{
    namespace fs = std::experimental::filesystem;

    class FileUtil
    {
    private:
        std::string _filename;
    public:
        FileUtil(const std::string &filename):_filename(filename){}
        bool Remove()
        {
            if(this->Exists() == false) return true; //文件不存在直接返回
            remove(_filename.c_str());
            return true;
        }
        
        int64_t FileSize() //文件大小
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout<<"get filen size failed!\n";
                return -1;
            }
            return st.st_size;
        }

        time_t LastMTime() //获取文件最后一次修改时间
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout<<"get filen LastMTime failed!\n";
                return -1;
            }
            return st.st_mtime;
        }

        time_t LastATime() //获取文件最后一次访问时间，根据这个时间判断该文件是否长时间没有使用，是否是非热点文件
        {
            struct stat st;
            if(stat(_filename.c_str(), &st) < 0)
            {
                std::cout<<"get filen LastATime failed!\n";
                return -1;
            }
            return st.st_atime;
        }
        std::string FileName() //获取文件路径名中的文件名称
        {
            //find_last_of由string类提供的接口，在该字符串中找到最后一个与之匹配的字符
            size_t pos = _filename.find_last_of("/");
            if(pos == std::string::npos)
            {
                return _filename;
            }
            //文件名
            return _filename.substr(pos + 1);
        }

        bool SetContent(const std::string &body) //向文件中写入数据（设置文件数据）
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            if(ofs.is_open() == false)
            {
                std::cout << "write open failed!" << std::endl;
                return false;
            }
            ofs.write(&body[0], body.size());
            //查看以上操作是否异常
            if(ofs.good() == false)
            {
                std::cout << "write file content failed!" << std::endl;
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }

        bool GetContent(std::string *body) //读取文件数据（获取文件数据）
        {
            size_t fsize = this->FileSize();
            return GetPostLen(body, 0, fsize);
        }

        bool GetPostLen(std::string *body, size_t pos, size_t len) //获取文件指定位置，指定长度数据
        {
            //计算文件大小
            size_t fsize = this->FileSize();
            //判断读取位置+读取长度是否大于文件
            if(pos + len > fsize)
            {
                std::cout << "get file len is error" << std::endl;
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename, std::ios_base::binary);
            if(ifs.is_open() == false)
            {
                std::cout << "read open file failed!" << std::endl;
                return false;
            }
            //将读写位置移动到pos处
            ifs.seekg(pos,std::ios_base::beg);
            body->resize(len);
            ifs.read(&(*body)[0],  len);
            if(ifs.good() == false)
            {
                std::cout << "get file content failed" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        bool Exists() //判断文件是否存在（主要判断目录是否存在，文件不存在会自动创建）
        {
            return fs::exists(_filename);
        }

        bool CreateDirectory() //创建目录
        {
            if(this->Exists()) return true;
            return fs::create_directories(_filename);
        }

        bool ScanDirectory(std::vector<std::string> *arry) //浏览目录获取目录下的文件信息
        {
            for(auto& p : fs::directory_iterator(_filename))
            {
                //目录中也可能存在文件夹
                if(fs::is_directory(p) == true)
                {
                    continue;
                }
                //relative_path 带有路径的文件名
                arry->push_back(fs::path(p).relative_path().string());
            }
            
            return true;
        }

        bool Compress(const std::string &packname) //非热点文件进行压缩处理，节省空间
        {
            //1.获取源文件数据
            std::string body;
            if(this->GetContent(&body) == false)
            {
                std::cout<<"compress get file content failed!\n";
                return false;
            }
            //2.对数据进行压缩
            std::string packed = bundle::pack(bundle::LZIP, body);
            //3.将压缩的数据存储到压缩文件中
            FileUtil fu(packname);
            if(fu.SetContent(packed) == false)
            {
                std::cout<<"compress write packed data failed!\n";
                return false;
            }
            return true;
        }

        bool UnCompress(const std::string &filename) //解压文件
        {
            std::string body;
            if(this->GetContent(&body) == false)
            {
                std::cout << "uncomptress get file content failed!\n";
                return false;
            }
            //将当前压缩的数据进行解压缩
            std::string unpacked = bundle::unpack(body);
            //将压缩的数据写入到新文件
            FileUtil fu(filename);
            if(fu.SetContent(unpacked) == false)
            {
                std::cout << "uncompress write packed data failed!\n";
                return false;
            }
            return true;
        } 
    };

    class JsonUtil
    {
    public:
        //序列化
        static bool Serialize(const Json::Value &root, std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            sw->write(root, &ss);
            *str = ss.str();
            return true;
        }

        //反序列化
        static bool UnSerialize(const std::string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string errs;
            bool ret = cr->parse(str.c_str(), str.c_str() + str .size(), root, &errs);
            if(ret == false)
            {
                std::cout << "error: " << errs << std::endl;
                return false;
            }

            return true;
        }
    };
}

#endif